package database;

import database.entity.User;
import com.healthmarketscience.jackcess.*;
import database.entity.Contact;
import database.entity.ContactGroup;
import database.entity.Mail;
import database.entity.MailBox;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.security.InvalidKeyException;
import java.text.ParseException;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Properties;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.mail.BodyPart;
import javax.mail.FetchProfile;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MailDateFormat;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.swing.DefaultListModel;
import org.apache.commons.codec.binary.Base64;
import security.AESKey;
import security.Hash;
import security.RSAKeyPair;
        
public class DatabaseWorker 
{
    static final String DATABASE_FILE="mailer.mdb";    
    static Database database=null;            
        
    public static Database getDatabase()
    {
        if (database==null)
        {
            database=connect();
        }
        return database;
    }
    
    private static Database connect()
    {
        try
        {
            return DatabaseBuilder.open(new File(DATABASE_FILE));
        }
        catch (IOException ex) 
        {
            ex.printStackTrace();
            System.exit(2);
        }
        return null;
    }
        
    public static LinkedList<String> getUserEmails()
    {
        LinkedList<String> result = new LinkedList<>();
        try 
        {            
            Table tbLogin = database.getTable("Login");
            Iterator<Row> ite = tbLogin.iterator();
            while (ite.hasNext())
            {
                Row r = ite.next();
                result.add(r.getString("Email"));
            }            
        } 
        catch (IOException ex) {
            ex.printStackTrace();
            //never happen because of correct table name
        }
        return result;
    }
    
    public static String extractEmail(String source)
    {
        int pos1 = source.indexOf("<");
        int pos2 = source.lastIndexOf(">");
        if (pos1>0)
            return source.substring(pos1+1, pos2);
        else
            return source;
    }

    public static boolean checkDB() {
        Database db = getDatabase();
        return (db!=null);
    }
    
    public static User checkLogin(String email, String password) throws IOException
    {
        Base64 base64 = new Base64();
        Table tbLogin = database.getTable("Login");         
        Row row = CursorBuilder.findRow(tbLogin, Collections.singletonMap("Email", email));
        if (row==null)
        {            
            return null;
        }
        else
        {
            //read seed
            String seed = row.getString("Seed");
            String hash = Hash.generateSHA256(password+seed);
            String hashedPassword = row.getString("Password");
            if (hash.equals(hashedPassword))
            {            
                //regenerate the encryptRSA key pair for decrypt aeskey
                RSAKeyPair encryptKey = new RSAKeyPair(password);            
                //regenerate AES Key for decrypt PrivateKey            
                AESKey aesKey = new AESKey(encryptKey.decodeByPrivateKey(base64.decode(row.getString("AESEncryptKey"))));

                return new User(row.getInt("UserID"),row.getString("Username"),row.getString("Email"),
                    password,
                    row.getString("PublicKey"),
                    row.getString("PrivateKey"),
                    row.getString("IncomingServer"), row.getString("Protocol"),
                    row.getString("OutgoingServer"), row.getString("Port"), row.getBoolean("SSL"),
                    row.getString("EmailPassword"),
                    row.getDate("LastSync"), row.getInt("SyncPeriod"), row.getString("AESEncryptKey"), aesKey);
            }
            else
            {
                return null;
            }
        }
    }
    
    User user;
    
    public DatabaseWorker(User main)
    {
        this.user=main;        
    }
    
    public MailBox[] getAllMailBox(boolean forceReload)
    {
        if (user.mailboxes==null || forceReload)
        {
            LinkedList<MailBox> result = new LinkedList<>();
            try 
            {
                Table tbMailBox = database.getTable("MailBox");
                Cursor cursor = CursorBuilder.createCursor(tbMailBox);
                while (cursor.findNextRow(Collections.singletonMap("UserID", user.UserID))) 
                {
                    Row row = cursor.getCurrentRow();
                    result.add(new MailBox(row.getInt("BoxID"), row.getString("BoxName")));
                }
                user.mailboxes = new MailBox[result.size()];
                user.mailboxes = result.toArray(user.mailboxes);
            } 
            catch (IOException ex) 
            {
                ex.printStackTrace();
                //never happen because of correct table name
            }
        }  
        return user.mailboxes;
    }
    
    public ContactGroup[] getAllContactGroup(boolean forceReload)
    {  
        Database db = getDatabase();
        if (user.contactgroup==null || forceReload)
        {
            LinkedList<ContactGroup> result = new LinkedList<>();
            try 
            {
                Table tbContactGroup = db.getTable("ContactGroup");
                Cursor cursor = CursorBuilder.createCursor(tbContactGroup);                                
                while (cursor.findNextRow(Collections.singletonMap("UserID", user.UserID))) 
                {              
                    Row row = cursor.getCurrentRow();
                    ContactGroup ct = new ContactGroup(row.getInt("GroupID"), row.getString("GroupName"));                    
                    result.add(ct);                    
                }
                user.contactgroup = new ContactGroup[result.size()];
                user.contactgroup = result.toArray(user.contactgroup);
            } 
            catch (IOException ex)
            {
                ex.printStackTrace();
                //never happen because of correct table name
            }  
        }
        return user.contactgroup;
    }
    
    public Mail[] loadMailFromBox(MailBox mailBox, boolean forceReload)
    {
        Database db = getDatabase();        
        Base64 base64 = new Base64();
        if (mailBox.mails==null || forceReload)
        {
            try
            {
                mailBox.mailModel = new DefaultListModel();
                LinkedList<Mail> mails = new LinkedList<>();               
                //load mails into this box
                Table tbMail = db.getTable("Mail");
                Cursor cursor = CursorBuilder.createCursor(tbMail);
                while (cursor.findNextRow(Collections.singletonMap("BoxID", mailBox.boxID)))
                {
                    Row r = cursor.getCurrentRow();
                    String mailSubject="<Le subject du message a ete endommage>";
                    String mailContent="<Le contenu du message a ete endommage>";
                    try
                    {
                        mailSubject=new String(user.aesKey.decode(base64.decode(r.getString("Subject"))));
                        mailContent=new String(user.aesKey.decode(base64.decode(r.getString("Content"))));
                    }
                    catch (BadPaddingException | IllegalBlockSizeException | InvalidKeyException | NoSuchPaddingException ex)
                    {
                        ex.printStackTrace();
                    }
                    Mail m =new Mail(r.getInt("MailID"), r.getInt("BoxID"), r.getString("From"),
                            r.getString("To"), r.getString("CC"), r.getString("BCC"), 
                            mailSubject, 
                            mailContent,
                            r.getDate("SentDate"), r.getDate("ReceivedDate"),
                            r.getBoolean("Signed"), r.getBoolean("Encrypted"), 
                            r.getString("HeaderField1"), r.getString("HeaderField2"), r.getString("HeaderField3"),
                            r.getBoolean("Unread"));
                    mails.add(m);
                    mailBox.mailModel.addElement(m);
                }
                mailBox.mails = new Mail[mails.size()];
                mails.toArray(mailBox.mails);                        
            }
            catch (IOException ex) 
            {
                ex.printStackTrace();
                return null;
            }                    
        }   
        //not null baecause we can always find out the box
        return mailBox.mails;
    }
    
    public boolean updateUserProperty(String propertyName, Object value)
    {       
        Database db = getDatabase();
        try 
        {
            Table tbLogin = db.getTable("Login");
            Cursor cursor = CursorBuilder.createCursor(tbLogin);            
            if (cursor.findNextRow(Collections.singletonMap("UserID", user.UserID)))
            {
                Row row = cursor.getCurrentRow();
                row.put(propertyName, value);
                tbLogin.updateRow(row);
            }
            //change value in user reference
            Class<?> c = user.getClass();
            Field field;
            try 
            {
                field = c.getField(propertyName);
                field.set(user, value);
            } 
            catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) 
            {
                //field is correct!
                ex.printStackTrace();
                return false;
            }                        
            return true;
        } 
        catch (IOException ex) 
        {       
            ex.printStackTrace();
            return false;
        }        
    }   

    public boolean deleteAllInfo()
    {    
        Database db = getDatabase();
        try 
        {            
            Table table;
            Table table2;
            Cursor cursor;
            Cursor cursor2;
            ////1. delete emails and mail boxes
            table = db.getTable("MailBox");
            table2 = db.getTable("Mail");
            
            cursor = CursorBuilder.createCursor(table);
            while (cursor.findNextRow(Collections.singletonMap("UserID", user.UserID)))                
            {
                Row r = cursor.getCurrentRow();
                int mailboxID = r.getInt("BoxID");
                cursor2 = CursorBuilder.createCursor(table2);
                while (cursor2.findNextRow(Collections.singletonMap("BoxID", mailboxID)))
                {
                    cursor2.deleteCurrentRow();
                }
                cursor.deleteCurrentRow();
            }
            
            ////2. delete contact and contact group
            table = db.getTable("ContactGroup");
            table2 = db.getTable("Contact");
            
            cursor = CursorBuilder.createCursor(table);
            while (cursor.findNextRow(Collections.singletonMap("UserID", user.UserID)))                
            {
                Row r = cursor.getCurrentRow();
                int groupID = r.getInt("GroupID");
                cursor2 = CursorBuilder.createCursor(table2);
                while (cursor2.findNextRow(Collections.singletonMap("GroupID", groupID)))
                {
                    cursor2.deleteCurrentRow();
                }
                cursor.deleteCurrentRow();
            }
            
            ////3. delete login info
            table = db.getTable("Login");
            cursor = CursorBuilder.createCursor(table);
            cursor.findNextRow(Collections.singletonMap("UserID", user.UserID));
            cursor.deleteCurrentRow();
            
            return true;
        }
        catch (IOException ex) 
        {            
            ex.printStackTrace();
            return false;
        }
    }   

    public Contact[] loadContactFromGroup(ContactGroup group, boolean forceReload)
    {
        Database db = getDatabase();
        if (group.contacts==null || forceReload)
        {
            try
            {
                group.contactModel = new DefaultListModel();
                LinkedList<Contact> contacts = new LinkedList<>();
                
                //load mails into this box
                Table tbContact = db.getTable("Contact");
                Cursor cursor = CursorBuilder.createCursor(tbContact);
                while (cursor.findNextRow(Collections.singletonMap("GroupID", group.groupID)))
                {
                    Row r = cursor.getCurrentRow();
                    Contact c =new Contact(r.getInt("ContactID"), r.getInt("GroupID"), r.getString("ContactName"),
                            r.getString("Email"), r.getString("PublicKey"));
                    contacts.add(c);
                    group.contactModel.addElement(c);

                }
                group.contacts = new Contact[contacts.size()];
                contacts.toArray(group.contacts);                        
            }
            catch (IOException ex) 
            {
                ex.printStackTrace();
                return null;
            }                    
        }
                
        return group.contacts;
    }
    
    public Contact getContactFromEmail(String email)
    {
        Contact result=null;
        ContactGroup[] cgs = getAllContactGroup(false);
        for (ContactGroup cg : cgs) 
        {
            Contact[] cts = loadContactFromGroup(cg,false);
            for (Contact ct : cts) 
            {
                if (ct.Email.equals(email))
                {
                    result=ct;                   
                    break;
                }
            }
        }
        return result;
    }
    
    public String getContactNameFromEmail(String email, boolean fullMode)
    {
        String result=email;
        Contact ct = getContactFromEmail(email);
        if (ct!=null)
        {
            result = ct.ContactName;
            if (fullMode)
                result+=" <"+email+">";
        }
        return result;
    }
    
    public ContactGroup getGroupFromGroupName(String groupName)
    {
        ContactGroup result=null;
        ContactGroup[] ctgs = getAllContactGroup(true);
        for (ContactGroup ctg : ctgs)
        {
            if (ctg.groupName.equals(groupName))
            {
                result=ctg;
                break;
            }
        }
        return result;
    }

    public void addContact(String name, String email, String groupName, String publicKey) 
    {
        try
        {
            Database db = getDatabase();
            Table tbContact =  db.getTable("Contact");
            ContactGroup group = getGroupFromGroupName(groupName);
            //ContactID, GroupID, ContactName, Email, PublicKey
            tbContact.addRow(null, group.groupID, name, email, publicKey);
            
            //refresh contact list of current group in user reference, force reload
            loadContactFromGroup(group, true);
        } 
        catch (IOException ex) 
        {
            ex.printStackTrace();
        }
    }

    public void updateContactProperties(Contact contact, String name, String email, String groupName, String publicKey) 
    {
        //update reference
        contact.ContactName=name;
        contact.Email=email;
        contact.GroupID=getGroupFromGroupName(groupName).groupID;
        contact.PublicKey=publicKey;
        
        //update database
        Database db = getDatabase();
        try 
        {
            Table tbContact = db.getTable("Contact");
            Cursor cursor = CursorBuilder.createCursor(tbContact);
            if (cursor.findNextRow(Collections.singletonMap("ContactID", contact.ContactID)))
            {
                Row row = cursor.getCurrentRow();
                row.put("ContactName", name);
                row.put("Email", email);
                row.put("GroupID", contact.GroupID);
                row.put("PublicKey", publicKey);
                tbContact.updateRow(row);
            }
            
        }
        catch (IOException ex) 
        {
            ex.printStackTrace();
        }
    }

    public ContactGroup addGroup(String groupName) 
    {
        ContactGroup result=null;
        try 
        {
            Database db = getDatabase();
            Table tbGroup = db.getTable("ContactGroup");
            //GroupID, UserID, GroupName
            Object[] row =tbGroup.addRow(null,user.UserID,groupName);
            
            result = new ContactGroup((int) row[0], groupName);
            //update reference
            getAllContactGroup(true);
        }
        catch (IOException ex)
        {
            ex.printStackTrace();
        }
        return result;
    }

    public void deleteGroup(int groupID) 
    {
        try 
        {
            Database db = getDatabase();
            Table tbContact = db.getTable("Contact");
            Table tbContactGroup = db.getTable("ContactGroup");
            
            //remove contacts
            Cursor cursor = CursorBuilder.createCursor(tbContact);
            while (cursor.findNextRow(Collections.singletonMap("GroupID", groupID)))
            {
                cursor.deleteCurrentRow();
            }
            cursor = CursorBuilder.createCursor(tbContactGroup);
            
            //remove contact group
            if (cursor.findNextRow(Collections.singletonMap("GroupID", groupID)))
            {
                cursor.deleteCurrentRow();
            }
        } 
        catch (IOException ex)
        {
            ex.printStackTrace();
        }
    }

    public void deleteMail(int MailID) 
    {
        try
        {
            Database db = getDatabase();
            Table tbMail = db.getTable("Mail");
            Cursor cursor = CursorBuilder.createCursor(tbMail);
            if (cursor.findNextRow(Collections.singletonMap("MailID", MailID)))
            {
                cursor.deleteCurrentRow();
            }
        }
        catch (IOException ex) 
        {
            ex.printStackTrace();
        }        
    }

    public void deleteContact(int ContactID) 
    {
        try
        {
            Database db = getDatabase();
            Table tbContact = db.getTable("Contact");
            Cursor cursor = CursorBuilder.createCursor(tbContact);
            if (cursor.findNextRow(Collections.singletonMap("ContactID", ContactID)))
            {
                cursor.deleteCurrentRow();
            }
        }
        catch (IOException ex) 
        {
            ex.printStackTrace();
        }
    }
    
    public void setMailRead(Mail m)
    {
        m.Unread=false;
        
        //update database
        try
        {
            Database db = getDatabase();
            Table tbMail = db.getTable("Mail");
            Cursor cursor = CursorBuilder.createCursor(tbMail);
            if (cursor.findNextRow(Collections.singletonMap("MailID", m.MailID)))
            {
                Row r = cursor.getCurrentRow();
                r.put("Unread", false);
                tbMail.updateRow(r);
            }
        }
        catch (IOException ex) 
        {
            ex.printStackTrace();
        }
    }

    public boolean syncMail()
    {
        //check incoming server        
        Properties props = System.getProperties();
        props.setProperty("mail.store.protocol", user.Protocol);           
        try 
        {
            Session session = Session.getInstance(props,
                    new javax.mail.Authenticator() 
                    {
                            @Override
                            protected PasswordAuthentication getPasswordAuthentication()
                            {
                                //thunder pig test email
                                return new PasswordAuthentication(user.Email,user.EmailPassword);
                            }
                    });
            Store store = session.getStore();

            store.connect(user.IncomingServer, user.Email,user.EmailPassword);
            
            //check and add new mail box
            Database db = getDatabase();
            Table tbMailBox = db.getTable("MailBox");
            Table tbEmail = db.getTable("Mail");
            
            Base64 base64 = new Base64();
            Folder[] folders = store.getDefaultFolder().list("*");
            for (Folder folder : folders)
            {
                if ((folder.getType() & Folder.HOLDS_MESSAGES & ~Folder.HOLDS_FOLDERS) != 0)    
                {
                    folder.open(Folder.READ_ONLY);
                    String folderName = folder.getFullName();
                    
                    //check if folder existed      
                    MailBox mb = getMailBoxFromName(folderName);
                    if (mb==null)
                    {
                        //not existed, add this as new box into database
                        //BoxID, UserID, BoxName
                        Object[] box = tbMailBox.addRow(null,user.UserID,folderName);
                        
                        //check for mails
                        Message[] msgs = folder.getMessages();
                        for (Message message : msgs) 
                        {
                            Mail m = getMailFromMessage(message);
                            tbEmail.addRow(null,box[0],m.From,m.To, m.CC,m.BCC, 
                                base64.encodeToString(user.aesKey.encode(m.Subject.getBytes())),
                                base64.encodeToString(user.aesKey.encode(m.Content.getBytes())),
                                m.SentDate, m.ReceivedDate, m.Signed, m.Encrypted, 
                                m.HeaderField1, m.HeaderField2, m.HeaderField3,m.Unread);
                        }

                        //reload
                        getAllMailBox(true);
                    }
                    else
                    {                        
                        //existed, check for new mails
                        //prefetch header to get info about date
                        FetchProfile fp = new FetchProfile();
                        fp.add(FetchProfile.Item.ENVELOPE);
                        Message[] messages=folder.getMessages();
                        folder.fetch(messages, fp);
                        for (Message message : messages) 
                        {
                            String receivedDate=message.getHeader("Date")[0];
                            MailDateFormat format = new MailDateFormat();
                            try 
                            {
                                Date d = format.parse(receivedDate);
                                //check if d > last sync date, so we synchronize
                                if (d.compareTo(user.LastSync)>0)
                                {
                                    Mail m = getMailFromMessage(message);                                   
                                    tbEmail.addRow(null,mb.boxID,m.From,m.To,m.CC,m.BCC,
                                        base64.encodeToString(user.aesKey.encode(m.Subject.getBytes())),
                                        base64.encodeToString(user.aesKey.encode(m.Content.getBytes())),
                                        m.SentDate,m.ReceivedDate,m.Signed,m.Encrypted,m.HeaderField1,m.HeaderField2,m.HeaderField3,m.Unread);
                                }
                            }                           
                            catch (ParseException ex)
                            {
                                ex.printStackTrace();
                                return false;
                            }
                        }
                        
                    }
                }
            }                                             
        }
        catch(MessagingException | IOException ex)
        {
            ex.printStackTrace();
            return false;
        }
        return true;
    }
    
    public static Mail getMailFromMessage(Message message) throws MessagingException, IOException
    {        
        //check if message is sent ou received
        String sender=InternetAddress.toString(message.getFrom());
        //check if sender contains other data
        if (sender.contains("<") && sender.contains(">"))
            sender = sender.substring(sender.indexOf("<")+1, sender.indexOf(">"));
        String To = InternetAddress.toString(message.getRecipients(Message.RecipientType.TO));
        String CC = InternetAddress.toString(message.getRecipients(Message.RecipientType.CC));
        String BCC = InternetAddress.toString(message.getRecipients(Message.RecipientType.BCC));                                                                                    
        boolean signed=false;
        boolean encrypted=false;
        String headerfield1="";
        String headerfield2="";
        String headerfield3="";
        String messagePlainText="";
  
        String[] header;
        header = message.getHeader("ThunderPig-header1");
        if (header!=null)                        
            headerfield1=header[0];
            
        header = message.getHeader("ThunderPig-header2");
        if (header!=null)                        
                headerfield2=header[0];
            
        header = message.getHeader("ThunderPig-header3");
        if (header!=null)                        
                headerfield3=header[0];
            
        header = message.getHeader("ThunderPig-signed");
        if (header!=null)
            signed = "yes".equals(header[0]);
        
        header = message.getHeader("ThunderPig-encrypted");
        if (header!=null)            
            encrypted = "yes".equals(header[0]);       

        //check for unread
        boolean unread = !message.getFlags().contains(Flags.Flag.SEEN);

        //get message content as plaintext
        Object content = message.getContent();
        if (content instanceof String)
        {
            messagePlainText=(String)content;
        }
        else
        {
            MimeMultipart mime = (MimeMultipart) message.getContent();
            for (int i=0;i<mime.getCount();i++)
            {
                BodyPart body = mime.getBodyPart(i);
                if (body.getContent() instanceof String)
                {
                    messagePlainText+=body.getContent();
                }
            }
        }               
        messagePlainText=messagePlainText.replaceAll("\r", "");        
        return new Mail(-1, -1, sender, To, CC, BCC, message.getSubject()==null? "<Sans objet>":message.getSubject(), messagePlainText, 
                message.getSentDate(), message.getReceivedDate(), signed, encrypted, headerfield1, headerfield2, headerfield3, unread);
    }
    
    private MailBox getMailBoxFromName(String name)
    {        
        for (MailBox mailboxe : user.mailboxes) 
        {
            if (mailboxe.boxName.equals(name)) 
            {
                return mailboxe;
            }
        }
        return null;
    }

    public void updateLastSync() 
    {
        Date lastSync = new Date(System.currentTimeMillis());
        user.LastSync=lastSync;

        //update database
        user.databaseWorker.updateUserProperty("LastSync", user.LastSync);        
    }
    
    public void sendMail(String subject, String content, String TO, String CC, String BCC, boolean isSign, boolean isEncrypt)
    {        
        Mail mail = new Mail();
        mail.From = user.Email;
        mail.To = TO;
        mail.CC = CC;
        mail.BCC = BCC;
        mail.Subject=subject;
        mail.Content=content+"\n";
        mail.Encrypted=isEncrypt;
        mail.Signed=isSign;
        
        if (TO!=null)
        {
            String[] TOs = TO.split(",");
            for (String to : TOs)
            {
                try 
                {
                    send(mail, to);
                } 
                catch (Exception ex)
                {
                    ex.printStackTrace();
                }
            }
        }
        
        if (CC!=null)
        {
            String[] CCs = CC.split(",");
            for (String cc : CCs)
            {
                try
                {
                    send(mail, cc);
                }
                catch (Exception ex)
                {
                    ex.printStackTrace();
                }
            }
        }
        
        if (BCC!=null)
        {
            String[] BCCs = BCC.split(",");
            for (String bcc : BCCs)
            {
                try 
                {
                    send(mail, bcc);
                } 
                catch (Exception ex) 
                {
                    ex.printStackTrace();
                }
            }
        }
    }
    
    private void send(Mail mail, String recipient) throws Exception
    {        
        Base64 base64 = new Base64();
        String messageHased = Hash.generateSHA256(mail.Content);
        //create new aek key
        AESKey aes = new AESKey(messageHased);
        
        Contact ct = getContactFromEmail(recipient);
        
        if (mail.Signed)
        {
            mail.HeaderField1 = base64.encodeToString(user.keypair.encodeByPrivateKey(messageHased.getBytes()));            
            if (mail.Encrypted)
            {
                //recontruct the public key of receiver
                RSAKeyPair rsa = new RSAKeyPair(base64.decode(ct.PublicKey), true);
                //insert headers
                byte[] aesEncoded = aes.getKeyEncoded();
                mail.HeaderField2 = base64.encodeToString(rsa.encodeByPublicKey(aesEncoded));
                mail.HeaderField3 = base64.encodeToString(user.keypair.encodeByPublicKey(aesEncoded));
                mail.Content = base64.encodeToString(aes.encode(mail.Content.getBytes()));                      
            }            
        }        
        doSendMail(mail, recipient);
    }
    
    private void doSendMail(Mail mail, String receiver) throws AddressException, MessagingException
    {
        //send message with SSL secure
        Properties props = new Properties();
        props.put("mail.smtp.host", user.OutgoingServer);
        
        if (user.SSL)
        {
            props.put("mail.smtp.socketFactory.port", user.Port);        
            props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        }
        
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.port", user.Port);

        Session session = Session.getInstance(props,
            new javax.mail.Authenticator() 
            {
                @Override
                protected PasswordAuthentication getPasswordAuthentication()
                {
                    return new PasswordAuthentication(user.Email,user.EmailPassword);
                }
            });

        Message message = new MimeMessage(session);                        
        message.setFrom(new InternetAddress(mail.From));
        if (mail.To!=null)
            message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(mail.To));
        if (mail.CC!=null)
            message.setRecipients(Message.RecipientType.CC, InternetAddress.parse(mail.CC));
        if (mail.BCC!=null)
            message.setRecipients(Message.RecipientType.BCC, InternetAddress.parse(mail.BCC));
        
        message.setSubject(mail.Subject);
        message.setText(mail.Content);
        message.addHeader("ThunderPig-header1", mail.HeaderField1);
        message.addHeader("ThunderPig-header2", mail.HeaderField2);
        message.addHeader("ThunderPig-header3", mail.HeaderField3);
        message.addHeader("ThunderPig-signed", mail.Signed? "yes":"no");
        message.addHeader("ThunderPig-encrypted", mail.Encrypted? "yes":"no");

        Transport.send(message,InternetAddress.parse(receiver));
    }
    
    private static String getListSeparatedByComma(String[] args)
    {
        String result="";
        if (args!=null)
        {
            if (args.length==1)
            {
                return args[0];
            }
            else
            {
                for (int i=0;i<args.length-1;i++)
                {
                    result+=args[i]+", ";
                }
                result+=args[args.length-1];                
            }
        }
        return result;
    }
}
